﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SpeakupBackend.Themes;
using TableParsers;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

namespace SpeakupTests.SpeakupBackend.Themes
{
    [TestClass]
    public class WorkshopClassTypeTests
    {
        static List<ClassType> InvalidClassTypes = new List<ClassType>();

        [ClassInitialize]
        public static void ClassInitialize(TestContext context)
        {
           
            foreach (ClassType classType in Enum.GetValues(typeof(ClassType)))
            {
                if (!WorkshopClassType.ValidClassTypes.Contains(classType))
                {
                    InvalidClassTypes.Add(classType);
                }
            }
        }

        [TestMethod]
        public void IsValidClassType()
        {
            foreach(ClassType classType in Enum.GetValues(typeof(ClassType)) )
            {
                Assert.AreEqual(WorkshopClassType.ValidClassTypes.Contains(classType), 
                                WorkshopClassType.IsValidClassType(classType));
            }
        }

        [TestMethod]
        public void GetWorkshopClassTypes()
        {
            IList<ClassType> validClassTypes = WorkshopClassType.ValidClassTypes;
            IList<WorkshopClassType> classTypes = WorkshopClassType.GetWorkshopClassTypes();

            Assert.AreEqual(classTypes.Count, validClassTypes.Count);

            foreach (ClassType validClassType in validClassTypes)
            {
                int count = (from n in classTypes
                             where n.ClassType == validClassType
                             select n).Count();

                Assert.AreEqual(1, count);
            }
        }
        
        [TestMethod]
        public void GetWorkshopClassTypeByEnum()
        {
            //verify that valid workshopClassTypes can be obtained
            foreach (ClassType classType in WorkshopClassType.ValidClassTypes)
            {
                WorkshopClassType th = WorkshopClassType.GetWorkshopClassType(classType);
                Assert.AreEqual(th.ClassType, classType);
            }

            //verify that invalid workshopClassTypes can not be obtained
            foreach (ClassType classType in InvalidClassTypes)
            {
                bool thrown = false;
                try
                {
                    WorkshopClassType.GetWorkshopClassType(classType);
                }
                catch (ArgumentException)
                {
                    thrown = true;
                }

                Assert.AreEqual(true, thrown);
            }
        }
        
        [TestMethod]
        public void GetWorkshopClassTypeByString()
        {
            //verify that valid WorkshopClassTypes can be obtained
            foreach (ClassType classType in WorkshopClassType.ValidClassTypes)
            {
                WorkshopClassType th = WorkshopClassType.GetWorkshopClassType(classType.ToString());
                Assert.AreEqual(th.ClassType, classType);
            }

            //verify that invalid WorkshopClassTypes can not be obtained
            foreach (ClassType classType in InvalidClassTypes)
            {
                bool thrown = false;
                try
                {
                    WorkshopClassType.GetWorkshopClassType(classType.ToString());
                }
                catch (ArgumentException)
                {
                    thrown = true;
                }

                Assert.AreEqual(true, thrown);
            }
        }
        
        [TestMethod]
        public void GetWorkshopClassTypeByInvalidString()
        {
            Random rnd = new Random();
            
            //verify that invalid WorkshopClassTypes can not be obtained
            foreach (ClassType classType in WorkshopClassType.ValidClassTypes)
            {
                bool thrown = false;
                try
                {
                    Char prefix = (Char)rnd.Next('a', 'c');
                    String name = String.Format("{0}{1}", prefix, classType.ToString());
                    WorkshopClassType th = WorkshopClassType.GetWorkshopClassType( name );
                }
                catch (FormatException)
                {
                    thrown = true;
                }

                Assert.AreEqual(true, thrown);
            }
        }

        
        [TestMethod]
        public void CreateWorkshopClassType()
        {
            //verify that valid WorkshopClassTypes can be created
            foreach (ClassType classType in WorkshopClassType.ValidClassTypes)
            {
                PrivateObject privateObject = new PrivateObject(typeof(WorkshopClassType), classType);
                WorkshopClassType workshopClassType = (WorkshopClassType)privateObject.Target;
                Assert.AreEqual(classType, workshopClassType.ClassType);
            }

            //verify that invalid WorkshopClassTypes can not be created
            foreach (ClassType classType in InvalidClassTypes)
            {
                bool thrown = false;
                try
                {
                    PrivateObject privateObject = new PrivateObject(typeof(WorkshopClassType), classType);
                }
                catch (TargetInvocationException exception)
                {
                    if (exception.InnerException.GetType() == typeof(ArgumentException))
                    {
                        thrown = true;
                    }
                }

                Assert.AreEqual(true, thrown);
            }
        }

        

        [TestMethod]
        public void WorkshopClassTypeToString()
        {
            //verify that each workshopClassType can be deserializedWorkshopClassType from string.
            foreach (ClassType classType in WorkshopClassType.ValidClassTypes)
            {
                WorkshopClassType original = WorkshopClassType.GetWorkshopClassType(classType);
                WorkshopClassType fromString = WorkshopClassType.GetWorkshopClassType(original.ToString());

                Assert.AreEqual(original.ClassType, fromString.ClassType);
            }
        }
        
        [TestMethod]
        public void WorkshopClassTypeObjectEquals()
        {
            //verify that all equal object return true for Equals (non-generic)
            foreach(ClassType classType in WorkshopClassType.ValidClassTypes)
            {
                PrivateObject privateWorkshopClassTypeA = new PrivateObject(typeof(WorkshopClassType), classType);
                PrivateObject privateWorkshopClassTypeB = new PrivateObject(typeof(WorkshopClassType), classType);

                Assert.IsTrue(privateWorkshopClassTypeA.Target.Equals(privateWorkshopClassTypeB.Target));
            }

            //verify that all non-equal return false for Equals (non-generic)
            foreach (ClassType i in WorkshopClassType.ValidClassTypes)
            {
                foreach (ClassType j in WorkshopClassType.ValidClassTypes)
                {
                    if (i == j) continue;

                    PrivateObject privateWorkshopClassTypeA = new PrivateObject(typeof(WorkshopClassType), i);
                    PrivateObject privateWorkshopClassTypeB = new PrivateObject(typeof(WorkshopClassType), j);

                    Assert.IsFalse(privateWorkshopClassTypeA.Target.Equals(privateWorkshopClassTypeB.Target));
                }
            }

            ClassType validClassType = WorkshopClassType.GetWorkshopClassType(WorkshopClassType.ValidClassTypes.First());

            //verify true for same object
            WorkshopClassType sameRefVerifier = WorkshopClassType.GetWorkshopClassType(validClassType);
            Object sameWorkshopClassType = sameRefVerifier;
            Assert.IsTrue(sameRefVerifier.Equals(sameWorkshopClassType));

            //verify that fasle returned for null
            WorkshopClassType nullVerifier = WorkshopClassType.GetWorkshopClassType(validClassType);
            Object nullObject = null;
            Assert.IsFalse(nullVerifier.Equals(nullObject));

            //verify false is returned for non-workshopClassType objects
            WorkshopClassType otherObjectVerifier = WorkshopClassType.GetWorkshopClassType(validClassType);
            Object otherObject = new Object();

            Assert.IsFalse(otherObjectVerifier.Equals(otherObject));
        }

        
        [TestMethod]
        public void WorkshopClassTypeIEquatableEquals()
        {
            //verify that all equal object return true for Equals (generic)
            foreach (ClassType classType in WorkshopClassType.ValidClassTypes)
            {
                PrivateObject privateWorkshopClassTypeA = new PrivateObject(typeof(WorkshopClassType), classType);
                PrivateObject privateWorkshopClassTypeB = new PrivateObject(typeof(WorkshopClassType), classType);

                WorkshopClassType workshopClassTypeA = (WorkshopClassType)privateWorkshopClassTypeA.Target;
                WorkshopClassType workshopClassTypeB = (WorkshopClassType)privateWorkshopClassTypeB.Target;

                Assert.IsTrue(privateWorkshopClassTypeA.Equals(privateWorkshopClassTypeB));
            }

            //verify that all non-equal return false for Equals (generic)
            foreach(ClassType i in WorkshopClassType.ValidClassTypes)
            {
                foreach (ClassType j in WorkshopClassType.ValidClassTypes)
                {
                    if (i == j) continue;

                    PrivateObject privateWorkshopClassTypeA = new PrivateObject(typeof(WorkshopClassType), i);
                    PrivateObject privateWorkshopClassTypeB = new PrivateObject(typeof(WorkshopClassType), j);

                    WorkshopClassType workshopClassTypeA = (WorkshopClassType)privateWorkshopClassTypeA.Target;
                    WorkshopClassType workshopClassTypeB = (WorkshopClassType)privateWorkshopClassTypeB.Target;

                    Assert.IsFalse(workshopClassTypeA.Equals(workshopClassTypeB));
                }
            }

            ClassType validClassType = WorkshopClassType.GetWorkshopClassType(WorkshopClassType.ValidClassTypes.First());

            //verify true for same object
            WorkshopClassType sameRefVerifier = WorkshopClassType.GetWorkshopClassType(validClassType);
            Assert.IsTrue(sameRefVerifier.Equals(sameRefVerifier));

            //verify that fasle returned for null
            WorkshopClassType nullVerifier = WorkshopClassType.GetWorkshopClassType(validClassType);
            WorkshopClassType nullWorkshopClassType = null;
            Assert.IsFalse(nullVerifier.Equals(nullWorkshopClassType));
        }

        
        [TestMethod]
        public void WorkshopClassTypeGetHashCode()
        {
            Dictionary<int, WorkshopClassType> workshopClassTypeDict = new Dictionary<int, WorkshopClassType>();

            //verify that no two workshopClassTypes have same hash code
            foreach (ClassType classType in WorkshopClassType.ValidClassTypes)
            {
                WorkshopClassType workshopClassType = WorkshopClassType.GetWorkshopClassType(classType);
                Assert.IsFalse(workshopClassTypeDict.Keys.Contains(workshopClassType.GetHashCode()));

                workshopClassTypeDict[workshopClassType.GetHashCode()] = workshopClassType;
            }
        }

        
        [TestMethod]
        public void WorkshopClassTypeObjectCompareTo()
        {
            foreach (ClassType i in WorkshopClassType.ValidClassTypes)
            {
                foreach (ClassType j in WorkshopClassType.ValidClassTypes)
                {
                    WorkshopClassType iWorkshopClassType = WorkshopClassType.GetWorkshopClassType(i);
                    WorkshopClassType jWorkshopClassType = WorkshopClassType.GetWorkshopClassType(j);

                    int i2j = iWorkshopClassType.CompareTo((Object)jWorkshopClassType);
                    int j2i = jWorkshopClassType.CompareTo((Object)iWorkshopClassType);

                    if (i < j)
                    {
                        Assert.IsTrue(i2j < 0);
                        Assert.IsTrue(j2i > 0);
                    }
                    else if (i > j)
                    {
                        Assert.IsTrue(i2j > 0);
                        Assert.IsTrue(j2i < 0);
                    }
                    else
                    {
                        Assert.AreEqual(0, i2j);
                        Assert.AreEqual(0, j2i);
                    }

                }
            }

        }        

        [TestMethod]
        public void WorkshopClassTypeGenericCompareTo()
        {
            foreach (ClassType i in WorkshopClassType.ValidClassTypes)
            {
                foreach (ClassType j in WorkshopClassType.ValidClassTypes)
                {
                    WorkshopClassType iWorkshopClassType = WorkshopClassType.GetWorkshopClassType(i);
                    WorkshopClassType jWorkshopClassType = WorkshopClassType.GetWorkshopClassType(j);

                    int i2j = iWorkshopClassType.CompareTo(jWorkshopClassType);
                    int j2i = jWorkshopClassType.CompareTo(iWorkshopClassType);

                    if (i < j)
                    {
                        Assert.IsTrue(i2j < 0);
                        Assert.IsTrue(j2i > 0);
                    }
                    else if (i > j)
                    {
                        Assert.IsTrue(i2j > 0);
                        Assert.IsTrue(j2i < 0);
                    }
                    else
                    {
                        Assert.AreEqual(0, i2j);
                        Assert.AreEqual(0, j2i);
                    }

                }
            }
        }

        
        [TestMethod]
        public void WorkshopClassTypeComaprisonOperators()
        {
            foreach (ClassType i in WorkshopClassType.ValidClassTypes)
            {
                foreach (ClassType j in WorkshopClassType.ValidClassTypes)
                {
                    WorkshopClassType iWorkshopClassType = WorkshopClassType.GetWorkshopClassType(i);
                    WorkshopClassType jWorkshopClassType = WorkshopClassType.GetWorkshopClassType(j);

                    Assert.AreEqual(i != j, iWorkshopClassType != jWorkshopClassType);
                    Assert.AreEqual(i == j, iWorkshopClassType == jWorkshopClassType);
                    Assert.AreEqual(i < j, iWorkshopClassType < jWorkshopClassType);
                    Assert.AreEqual(i <= j, iWorkshopClassType <= jWorkshopClassType);
                    Assert.AreEqual(i > j, iWorkshopClassType > jWorkshopClassType);
                    Assert.AreEqual(i >= j, iWorkshopClassType >= jWorkshopClassType);

                    Assert.AreEqual(j != i, jWorkshopClassType != iWorkshopClassType);
                    Assert.AreEqual(j == i, jWorkshopClassType == iWorkshopClassType);
                    Assert.AreEqual(j < i, jWorkshopClassType < iWorkshopClassType);
                    Assert.AreEqual(j <= i, jWorkshopClassType <= iWorkshopClassType);
                    Assert.AreEqual(j > i, jWorkshopClassType > iWorkshopClassType);
                    Assert.AreEqual(j >= i, jWorkshopClassType >= iWorkshopClassType);
                }
            }

            ClassType validClassType = WorkshopClassType.GetWorkshopClassType(WorkshopClassType.ValidClassTypes.First());

            WorkshopClassType iNullWorkshopClassType = null;
            WorkshopClassType jNullWorkshopClassType = null;
            WorkshopClassType nonNullWorkshopClassType = WorkshopClassType.GetWorkshopClassType(validClassType);

            Assert.IsTrue(iNullWorkshopClassType == jNullWorkshopClassType);
            Assert.IsTrue(jNullWorkshopClassType == iNullWorkshopClassType);

            Assert.IsFalse(iNullWorkshopClassType != jNullWorkshopClassType);
            Assert.IsFalse(jNullWorkshopClassType != iNullWorkshopClassType);

            Assert.IsFalse(nonNullWorkshopClassType == iNullWorkshopClassType);
            Assert.IsFalse(iNullWorkshopClassType == nonNullWorkshopClassType);

            Assert.IsTrue(nonNullWorkshopClassType != iNullWorkshopClassType);
            Assert.IsTrue(iNullWorkshopClassType != nonNullWorkshopClassType);
        }

        
        [TestMethod]
        public void WorkshopClassTypeSerialization()
        {
            BinaryFormatter formatter = new BinaryFormatter();

            foreach (ClassType i in WorkshopClassType.ValidClassTypes)
            {
                using (MemoryStream memStream = new MemoryStream())
                {
                    WorkshopClassType workshopClassType = WorkshopClassType.GetWorkshopClassType(i);

                    formatter.Serialize(memStream, workshopClassType);
                    memStream.Position = 0;
                    WorkshopClassType deserializedWorkshopClassType = (WorkshopClassType)formatter.Deserialize(memStream);

                    Assert.AreEqual(workshopClassType.ClassType, deserializedWorkshopClassType.ClassType);
                    Assert.IsTrue(Object.ReferenceEquals(workshopClassType, deserializedWorkshopClassType));
                }
            }
        }

    }
}
